package jp.reflexworks.invoice;

import java.io.IOException;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.jdo.JDOException;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

import jp.reflexworks.gae.servlet.ReflexServlet;
import jp.reflexworks.gae.util.HttpStatus;
import jp.reflexworks.gae.util.KeyUtils;
import jp.reflexworks.gae.util.PMF;
import jp.reflexworks.gae.util.RequestMapper;
import jp.reflexworks.gae.util.Status;
import jp.reflexworks.invoice.model.Invoice;
import jp.reflexworks.invoice.model.InvoiceBase;
import jp.reflexworks.invoice.model.Order;
import jp.reflexworks.invoice.util.JdoUtils;
import jp.reflexworks.invoice.util.OrderConverter;
import jp.sourceforge.reflex.util.FieldMapper;

@SuppressWarnings("serial")
public class InvoiceServlet extends ReflexServlet {

	private static final String BR = System.getProperty("line.separator");
	
	Logger logger = Logger.getLogger(this.getClass().getName());
	private static final String	MODEL_PACKAGE	= "jp.reflexworks.invoice.model";
	
	KeyUtils keyUtils = new KeyUtils(InvoiceBase.class);
	
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		String INVOICE_NO = "MNS200904_009";
		JdoUtils jdoUtils = new JdoUtils();
		OrderConverter converter = new OrderConverter();

/*
 * 2009-08-05
 * InvoiceBaseとInvoiceからgetObjectByIdで直接レコードを取得できるかのテスト
 */
if (false) {
	
	PersistenceManager pm = PMF.get().getPersistenceManager();
	Transaction tx = pm.currentTransaction();
	
	tx.begin();

	Class baseKind;
	
	baseKind = InvoiceBase.class;
	String kind = baseKind.getSimpleName();
	String name = kind;
	KeyFactory.Builder keyBuilderInvoiceBase = new KeyFactory.Builder(kind, name);
	
	Key keyInvoiceBase = keyBuilderInvoiceBase.getKey();
	
	String invoiceNo = "MNS200904_001";
	Key keyInvoice = keyBuilderInvoiceBase.addChild((Invoice.class).getSimpleName(), "K"+invoiceNo).getKey();
	
	//order用
	DecimalFormat orderIdFormat = new DecimalFormat("00000000000000000000");

	InvoiceBase invoiceBase = pm.getObjectById(InvoiceBase.class, keyInvoiceBase);
	
	Invoice invoice = pm.getObjectById(Invoice.class, keyInvoice);
	
	//invoice.setKey(keyInvoice);
	//invoice.setLineTotal("888");
	//invoiceBase.setInvoiceId(998L);
	
	int num = 3;
	
	Long orderId = invoiceBase.getOrderId();
	orderId = 0L;
	List<Order> listOrder = new ArrayList<Order>();
	for (int i = 0; i < num; i++) {
		++orderId;
		
		keyBuilderInvoiceBase = new KeyFactory.Builder(kind, name);
		Key keyOrder = keyBuilderInvoiceBase.addChild((Invoice.class).getSimpleName(), "K"+invoiceNo).addChild((Order.class).getSimpleName(), "ID" + orderIdFormat.format(orderId)).getKey();


		//Order order = pm.getObjectById(Order.class, keyOrder);
		Order order = new Order();
		order.setKey(keyOrder);
		order.setId(orderId);
		
		debug("keyOrder: " + keyOrder);
		debug("orderId: " + orderId);
		
		//order.invoiceNo   = invoiceNo;
		order.productcd   = "XXXXproductcd_" + String.format("%04d", i);
		
		pm.makePersistent(order);
		
		//成功する
		//invoice.addOrder(order);
		
		//twiceエラー
		//listOrder.add(order);
		
		//エラーは出ないが書き換わらない
		if (invoice.order == null) {
			invoice.order = new ArrayList<Order>();
		} else {
			invoice.order.add(order);
		}
	}
	//twiceエラー
	//invoice.setOrder(listOrder);
	
	invoiceBase.setOrderId(orderId);
	
	tx.commit();
	pm.close();
	
	resp.getWriter().print("TEST 2009-08-05");
}





		

/////////////////////////////////////////////////////////////////////////////////////////////////////	
		
		
		
		
		
		

		boolean toJSON = true;
		if (req.getParameter("xml") != null) {
			toJSON = false; // toJSON
		}

		try {
			Invoice param = new Invoice();
			(new RequestMapper()).setValue(req, param);
			
			String insertCntStr = req.getParameter("init");
			if (insertCntStr != null) {

				String[] idx = null;
				if (insertCntStr.indexOf("-") > 0) {
					idx = insertCntStr.split("-");
				} else {
					idx = new String[2];
					idx[0] = "1";
					idx[1] = insertCntStr;
				}

				List<Invoice> recList = new ArrayList<Invoice>();
				for (long i = Long.parseLong(idx[0]); i <= Long.parseLong(idx[1]); i++) {
					Invoice rec = Instance.newInvoice(i);
					recList.add(rec);
				}
				jdoUtils.insert2(recList);

				Status status = new Status();
				status.code = HttpStatus.SC_CREATED;
				status.message = "Created.";
				doResponse(resp, status, toJSON, MODEL_PACKAGE);
			} else {
				
				
				
				
				//2009-08-05 mura updateのテスト
				String update = req.getParameter("update");
				if (update != null) {
					System.out.println("############################update test############################");
					
					List<Invoice> recList = new ArrayList<Invoice>();
					String[] idx = new String[2];
					idx[0] = "1";
					idx[1] = "3";
					//for (long i = Long.parseLong(idx[0]); i <= Long.parseLong(idx[1]); i++) {
						Invoice rec = Instance.newInvoiceForUpdate(1);
						recList.add(rec);
					//}
					
					jdoUtils.update2(rec);
					
					resp.getWriter().print("UPDATE");
					
				
				
				} else {
				
				
				
				
				
					// idで検索getEntriesById
					String id = req.getParameter("id");
					if (id != null) {
						Invoice invoice = jdoUtils.getInvoiceById(param);
	
						if (invoice == null) {
							Status status = new Status();
							status.code = HttpStatus.SC_NO_CONTENT;
							status.message = "No data.";
							try {
								doResponse(resp, status, toJSON, MODEL_PACKAGE);
							} catch (Exception ee) {
								ee.printStackTrace();
							}
						} else {
							doResponse(resp, invoice, toJSON, MODEL_PACKAGE); // true:JSON
						}
						doResponse(resp, invoice, toJSON, MODEL_PACKAGE); // true:JSON
					} else if (param.invoiceNo != null) {
	
						
						Invoice invoice = jdoUtils.getInvoiceByKey(param);
						List<Order> listOrder = jdoUtils.getOrdersByInvoiceNo(invoice.getInvoiceNo());
						
	
						invoice.setOrder(converter.reduce(listOrder));
						doResponse(resp, invoice, toJSON, MODEL_PACKAGE);
						
					} else {
	
						String invoiceNo = req.getParameter("record");
						if (invoiceNo != null) {
							List<Order> recordList = jdoUtils.getOrdersByInvoiceNo(invoiceNo);
	
							if (recordList == null || recordList.size() == 0) {
								Status status = new Status();
								status.code = HttpStatus.SC_NO_CONTENT;
								status.message = "No data.";
								try {
									doResponse(resp, status, toJSON, MODEL_PACKAGE);
								} catch (Exception ee) {
									ee.printStackTrace();
								}
							} else {
								Invoice invoice = new Invoice();
								invoice.setOrder(recordList);
	
								doResponse(resp, invoice, toJSON, MODEL_PACKAGE); // true:JSON
							}
						} else {
	
							// 各項目で検索
	
							(new RequestMapper()).setValue(req, param);
							InvoiceBase invoiceBase = new InvoiceBase();
							String pagesize = req.getParameter("pagesize");
							String nextid = req.getParameter("nextid");
							invoiceBase.invoiceList = jdoUtils.getEntriesByParam(param, pagesize, nextid);
	
							if (invoiceBase.invoiceList == null || invoiceBase.invoiceList.size() == 0) {
								Status status = new Status();
								status.code = HttpStatus.SC_NO_CONTENT;
								status.message = "No data.";
								try {
									doResponse(resp, status, toJSON, MODEL_PACKAGE);
								} catch (Exception ee) {
									ee.printStackTrace();
								}
							} else {
								//jdoUtils.complementInvoiceRecord(invoiceBase.invoiceList);
								doResponse(resp, invoiceBase, toJSON, MODEL_PACKAGE); // true:JSON
							}
						}
					}
				}
			
			}

			
			
			
			
			
			
			
			
			
			
			
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
			Status status = new Status();
			status.code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
			status.message = e.getMessage();
			try {
				doErrorPage(resp, e);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}
	}

	public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		boolean toXml = true;
		if (req.getParameter("xml") != null) {
			toXml = false; // toJSON
		}

		try {

			JdoUtils jdoUtils = new JdoUtils();
			Invoice invoice = (Invoice) getEntity(req, MODEL_PACKAGE);

			try {
				jdoUtils.getInvoiceByKey(invoice);

				// すでに登録されている場合エラー
				Status status = new Status();
				status.code = HttpStatus.SC_CONFLICT;
				status.message = "PK already exists.";
				doResponse(resp, status, toXml, MODEL_PACKAGE);

			} catch (JDOObjectNotFoundException e) {

				// 登録されていなければinsert
//				Invoice invoicenew = Instance.newInvoice(1);
				Invoice invoicenew = new Invoice();
				invoicenew.setInvoiceNo(invoice.invoiceNo); 
				jdoUtils.insert(invoicenew);
				doResponse(resp, invoicenew, toXml, MODEL_PACKAGE, HttpStatus.SC_CREATED);

			}

		} catch (Exception e) {
			e.printStackTrace();
			Status status = new Status();
			status.code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
			status.message = e.getMessage();
			try {
				doErrorPage(resp, e);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}

	}

	public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		logger.info("put begin.");

		boolean toXml = true;
		if (req.getParameter("xml") != null) {
			toXml = false; // toJSON
		}

		try {
			Invoice invoice = (Invoice) getEntity(req, MODEL_PACKAGE);

			logger.info("update step1.");
			// update
			JdoUtils jdoUtils = new JdoUtils();
			
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////v
////mura
			debug("ここでupdate()呼び出し invoiceNo:"+invoice.order.get(0).invoiceNo);
//			jdoUtils.update(invoice);
			jdoUtils.update2(invoice);
			debug("ここでupdate()呼び出し終了");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////v
			
			
			logger.info("update step2.");
			Status status = new Status();
			status.code = HttpStatus.SC_OK;
			status.message = "Updated.";
			doResponse(resp, status, toXml, MODEL_PACKAGE);

		} catch (ConcurrentModificationException e) {
			// 楽観的ロック失敗
			logger.info("update step3.");
			Status status = new Status();
			status.code = HttpStatus.SC_CONFLICT;
			status.message = "Optimistic locking failed.";
			try {
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			} catch (Exception ee) {
				ee.printStackTrace();
			}

		} catch (JDOException e) {
			// 更新エラー
			logger.info("update step4.");
			Status status = new Status();
			status.code = HttpStatus.SC_CONFLICT;
			status.message = "Updating failed.";
			try {
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		} catch (Exception e) {
			logger.info("update step5.");
			e.printStackTrace();
			Status status = new Status();
			status.code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
			status.message = e.getMessage();
			try {
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}

	}

	public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		boolean toXml = true;
		if (req.getParameter("xml") != null) {
			toXml = false; // toJSON
		}

		try {

			// delete completely
			String completely = req.getParameter("completely");
			if (completely != null) {
				JdoUtils jdoUtils = new JdoUtils();

				String[] idx = null;
				if (completely.indexOf("-") > 0) {
					idx = completely.split("-");
				} else {
					idx = new String[2];
					idx[0] = "1";
					idx[1] = completely;
				}

				for (long i = Long.parseLong(idx[0]); i <= Long.parseLong(idx[1]); i++) {
					Invoice rec = new Invoice();
					rec.id = i;
					jdoUtils.deleteCompletely(rec);
				}

				Status status = new Status();
				status.code = HttpStatus.SC_OK;
				status.message = completely + " cases were deleted.";
				doResponse(resp, status, toXml, MODEL_PACKAGE);

				return;
			}

			// パラメータからの値を取得
			Invoice param = new Invoice();
			(new RequestMapper()).setValue(req, param);

			// delete
			JdoUtils jdoUtils = new JdoUtils();

			// 削除対象が存在しない場合
			if (jdoUtils.getInvoiceByKey(param) == null) {
				Status status = new Status();
				status.code = HttpStatus.SC_NO_CONTENT;
				status.message = "No content.";
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			} else {
				jdoUtils.delete(param);
				Status status = new Status();
				status.code = HttpStatus.SC_OK;
				status.message = "Deleted.";
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			}

		} catch (Exception e) {
			e.printStackTrace();
			Status status = new Status();
			status.code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
			status.message = e.getMessage() + "\n";
			try {
				doResponse(resp, status, toXml, MODEL_PACKAGE);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}
	}
	
	private void debug(Object message) {
		//System.out.println("DEBUG " + this.getClass().getName() +  " :" + message);
		System.out.println(message);
	}
	
	private String inspectOrder(Order order) {
		StringBuilder sb = new StringBuilder();
		
		sb.append("## debugOrder-- ##" + BR);
		sb.append("key: " + order.getKey() + BR);
		sb.append("id: " + order.getId() + BR);
		sb.append("invoiceNo: " + order.invoiceNo + BR);
		sb.append("productcd: " + order.productcd + BR);
		sb.append("deleted: " + order.deleted + BR);
		
		sb.append("description: " + order.description + BR);
		sb.append("lineTotal: " + order.lineTotal + BR);
		sb.append("quantity: " + order.quantity + BR);
		sb.append("seqno: " + order.seqno + BR);
		sb.append("unitPrice: " + order.unitPrice + BR);
		sb.append("## --debugOrder ##" + BR);
		
		return sb.toString();
	}
	
	private String inspectListOrder(List<Order> listOrder) {
		StringBuilder sb = new StringBuilder();
		
		int len = listOrder.size();
		
		sb.append("## debugListOrder-- ##" + BR);
		sb.append("listOrder.size(): " + len + BR);
		
		for (int i = 0; i < len; i++) {
			sb.append("#order(" + i + ")--#" + BR);
			sb.append(inspectOrder(listOrder.get(i)));
			sb.append("#--order(" + i + ")#" + BR);
		}

		sb.append("## --debugListOrder ##" + BR);
		
		return sb.toString();
	}
	
	private String inspectInvoice(Invoice invoice) {
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("## debugInvoice-- ##" + BR);
		
		if (invoice.order != null) {
			sb.append("orderList.size(): " + invoice.order.size() + BR);
		} else {
			sb.append("orderList.size(): orderListがNULLです" + BR);
		}
		
		sb.append("key: " + invoice.getKey() + BR);
		sb.append("revision: " + invoice.revision + BR);
		sb.append("id: " + invoice.id + BR);
		sb.append("invoiceNo: " + invoice.invoiceNo + BR);
		sb.append("deleted: " + invoice.deleted + BR);
		
		
		sb.append("companyName: " + invoice.companyName + BR);
		sb.append("address: " + invoice.address + BR);
		sb.append("description: " + invoice.description + BR);
		sb.append("invoiceTitle: " + invoice.invoiceTitle + BR);
		sb.append("issuedDate: " + invoice.issuedDate + BR);
		sb.append("job: " + invoice.job + BR);
		sb.append("lineTotal: " + invoice.lineTotal + BR);
		sb.append("myCompanyName: " + invoice.myCompanyName + BR);
		sb.append("note: " + invoice.note + BR);
		sb.append("note1: " + invoice.note1 + BR);
		sb.append("note2: " + invoice.note2 + BR);
		sb.append("note3: " + invoice.note3 + BR);
		sb.append("phone: " + invoice.phone + BR);
		sb.append("quantity: " + invoice.quantity + BR);
		
		sb.append("salestax: " + invoice.salestax + BR);
		sb.append("salestax1: " + invoice.salestax1 + BR);
		sb.append("seqno: " + invoice.seqno + BR);
		sb.append("subtotal: " + invoice.subtotal + BR);
		sb.append("subtotal1: " + invoice.subtotal1 + BR);
		sb.append("summary: " + invoice.summary + BR);
		sb.append("title: " + invoice.title + BR);
		sb.append("total: " + invoice.total + BR);
		sb.append("totalTd: " + invoice.totalTd + BR);
		sb.append("totalTd1: " + invoice.totalTd1 + BR);
		sb.append("unitPrice: " + invoice.unitPrice + BR);
		sb.append("zip: " + invoice.zip + BR);
		
		
		
		if (invoice.order != null) {
			sb.append(BR + BR);
			sb.append(inspectListOrder(invoice.order));
		} else {
			sb.append("orderListがNULLのためinspectListOrder()は実行されません" + BR);
		}
		
		sb.append("## --debugInvoice ##" + BR);
		
	
		return sb.toString();
	}
	
}
